package com.ruolin.micro.gateway.server.route.resolver;

import com.ruolin.micro.gateway.server.constant.FormType;
import com.ruolin.micro.gateway.server.metadata.MethodRouteMetadata;
import com.ruolin.micro.gateway.server.metadata.ParameterMetadata;
import com.ruolin.micro.gateway.server.route.RouteDefinitionLocator;
import com.ruolin.micro.gateway.server.servlet.bean.RouterBeanDefinition;
import com.ruolin.micro.gateway.server.utils.DataTypePatternUtils;
import com.ruolin.micro.gateway.server.utils.ParameterUtils;
import org.springframework.beans.factory.annotation.Autowired;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * rest request resolver, to usr to resolve for example /hello/{name}.
 * If parameter contain some filed and a object field ,that dose not support
 * such as http://localhost:8080/hello/{name}?id=1&objId=1&objName=name
 *
 * @author zhaoshengjie
 */

public class PathVariableParameterResolver extends AbstractRequestParameterResolver {
    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;

    @Override
    public Object[] resolve(HttpServletRequest request, HttpServletResponse response) {
        String requestRoute = request.getPathInfo();
        List<RouterBeanDefinition> routeDefinitions = routeDefinitionLocator.getRouteDefinitions();

        MethodRouteMetadata methodRouteMetadata = getMethodRouteMetadata(requestRoute, routeDefinitions);


        List<String> types = methodRouteMetadata.getParameterTypes();
        if (!methodRouteMetadata.hasJavaType()) {
            throw new IllegalArgumentException("parameter type is not match,route");
        }

        //TODO
        RouterBeanDefinition routerBeanDefinition = getRouterBeanDefinition(requestRoute, routeDefinitions);
        String tmpRoute = routerBeanDefinition.getPath().concat("/").concat(methodRouteMetadata.getName());
        String valueStr = requestRoute.substring(tmpRoute.length() + 1);
        String[] values = valueStr.split("/");

        String queryString = request.getQueryString();
        if (queryString == null) {
            return values;
        }

        List<ParameterMetadata> parameters = ParameterUtils.getParameters(queryString);
        Map<String, Object> fieldMap = methodRouteMetadata.getFieldMap();
        Map<String, Object> parameterMap = ParameterUtils.buildParameterMap(parameters);
        int typeLen = methodRouteMetadata.getTypeLen();
        Object[] args = null;
        int parameterLen = values.length + parameters.size();
        if (fieldMap.isEmpty()) {
            if (parameterLen == typeLen) {
                Collection<Object> parameterValues = parameterMap.values();

                List<Object> pathValues = Arrays.stream(values).collect(Collectors.toList());
                pathValues.addAll(parameterValues);
                args = pathValues.toArray();

                return args;
            } else {

                throw new IllegalArgumentException("args.length != types.length");
            }

        } else {
            int argLen = values.length + 1;
            fieldMap = ParameterUtils.convertObjectMap(parameterMap, fieldMap);

            if (parameterMap.size() > 0 && parameterMap.size() != argLen) {
                throw new IllegalArgumentException("parameter data types do not match");
            }

            if (methodRouteMetadata.getObjectIndex() == 0) {
                args = new Object[]{fieldMap};
                return mergeArgValues(args, values);
            } else {
                args = new Object[types.size()];
                List<String> pathValues = Arrays.stream(values).collect(Collectors.toList());
                for (int i = 0; i < types.size() - 1; i++) {
                    Object pValue = pathValues.get(i);
                    if (valueStr.getClass().getName().equals(types.get(i)) || DataTypePatternUtils.dataTypeMatch(types.get(0), pValue)) {
                        args[i] = pathValues.get(i);
                    } else {
                        throw new IllegalArgumentException("parameter data types do not match,type:::: " + pathValues.get(i).getClass().getName());
                    }
                }

                args[args.length - 1] = fieldMap;
            }
        }

        parameterMap.clear();
        return args;
    }

    private Object[] mergeArgValues(Object[] arg0, Object[] arg1) {
        List<Object> args0 = Arrays.stream(arg0).collect(Collectors.toList());
        args0.addAll(Stream.of(arg1).collect(Collectors.toList()));
        return args0.toArray();
    }

    @Override
    public PathVariableParameterResolver getResolver() {
        return this;
    }

    @Override
    public FormType getFormType() {
        return FormType.PATH;
    }
}